#pragma once
#include <etcd/Client.hpp>
#include <etcd/KeepAlive.hpp>
#include <etcd/Response.hpp>
#include <etcd/Value.hpp>
#include <etcd/Watcher.hpp>
#include <functional>
#include "logger.hpp"
namespace wsll
{
    // 服务注册客户端类

    class Registry
    {
    public:
        using ptr = std::shared_ptr<Registry>;
        Registry(const std::string &host)                    // host为提供的服务器地址。这里的服务器是指etcd服务器，这个服务器是自动运行的
            : _client(std::make_shared<etcd::Client>(host)), // 这里的host一般就是etcd的2379端口。我们可以说clint是与etcd服务器进行通信的两台主机（包括服务注册客户端和服务发现客户端），也代表这个2379端口
              _keep_alive(
                  _client->leasekeepalive(3).get()) // 只保活3s，后期应该可以修改
              ,
              _lease_id(_keep_alive->Lease()) // 所谓租约，租约相当于服务注册client与etcd签订了房租租约，并且规定了有效时长。一旦超时，etcd会自动删除租约（也就是把客户端标记为失活），从而通过watcher通知服务发现客户端
        {
        }
        ~Registry() { _keep_alive->Cancel(); }                          // 主动取消租约
        bool registry(const std::string &key, const std::string &value) // 新增数据，可以想象成etcd服务器底下有一个目录/service，然后我们客户端都是为了针对这个目录进行操作。key就是目录下的文件名，value就是文件内容，而文件内容在这里表示“可以提供服务的ip”
        {
            auto resp = _client->put(key, value, _lease_id).get(); // response的含义可以查到
            if (resp.is_ok() == false)
            {
                LOG_ERROR("注册数据失败：{}", resp.error_message());
                return false;
            }
            return true;
        }

    private:
        std::shared_ptr<etcd::Client> _client;
        std::shared_ptr<etcd::KeepAlive> _keep_alive;
        uint64_t _lease_id;
    };
    // 服务发现客户端类

    class Discovery
    {
    public:
        using ptr = std::shared_ptr<Discovery>;
        // 回调函数,第一个参数是将要上线的服务名称，第二个参数是服务对应的主机地址。
        using NotifyCallback =
            std::function<void(std::string, std::string)>;
        // basedir:注册的根目录 put_cb新增对应的处理函数 del_cb删除对应的处理函数
        Discovery(const std::string &host, const std::string basedir,
                  const NotifyCallback &put_cb, const NotifyCallback &del_cb)
            : _client(std::make_shared<etcd::Client>(host)),

              _put_cb(put_cb),
              _del_cb(del_cb)
        {
            // 服务发现，先获取到当前已有的数据
            auto resp = _client->ls("/service").get(); // 这里可能获得多个键值对,因此需要遍历。其实就是一段时间来可能监控到多个事件改变
            if (resp.is_ok() == false)
            {
                LOG_ERROR("获取服务信息数据失败:{}", resp.error_message());
            }
            int sz = resp.keys().size();
            for (int i = 0; i < sz; i++)
            {
                if (_put_cb)
                    _put_cb(resp.keys()[i], resp.values()[i].as_string());
            }
            // 进行事件监控，当有数据变化时，调用回调函数
            _watcher = std::make_shared<etcd::Watcher>(
                *_client.get(), basedir,
                std::bind(&Discovery::callback, this, std::placeholders::_1), // 这里的作用可以看示例程序。通俗来说就是一旦wachter收到了etcd服务器的通知，就会调用这个函数
                true);                                                        // true代表递归监控，也就是说监控basedir下的所有子目录
        }
        ~Discovery()
        {
            _watcher->Cancel();
            // if(_watcher->Cancelled())LOG_DEBUG("the _watcher has been cancelled");
            // _watcher->Wait();
        }

    private:
        void callback(const etcd::Response &resp)
        {
            try
            {
                if (resp.is_ok() == false)
                {
                    LOG_ERROR("watcher失败,收到一个错误的事件通知{}", resp.error_message());
                    return;
                }
                for (const auto &event : resp.events())
                {
                    if (event.event_type() == etcd::Event::EventType::PUT)
                    {
                        if (_put_cb)
                            _put_cb(event.kv().key(), event.kv().as_string());
                        LOG_DEBUG("新增服务:{}-{}", event.kv().key(), event.kv().as_string());
                    }
                    else if (event.event_type() == etcd::Event::EventType::DELETE_)
                    {
                        if (_del_cb)
                            _del_cb(event.prev_kv().key(), event.prev_kv().as_string());
                        LOG_DEBUG("服务下线:{}-{}", event.prev_kv().key(), event.prev_kv().as_string());
                    }
                }
            }
            catch (const std::exception &e)
            {
                LOG_ERROR("Watcher 回调异常: {}", e.what());
            }
        }
        std::shared_ptr<etcd::Client> _client;
        std::shared_ptr<etcd::Watcher> _watcher;
        NotifyCallback _put_cb;
        NotifyCallback _del_cb;
    };
}